/**
 * ###根据hibernate-core-5.2.17.Final.jar源码进行调整,如该jar包版本有变化,需要重新下载源码进行调整,并做好注释####
 * 
 * 特重要说明:
 * 	 1.为了解决枚举类类型BaseIntEnum、BaseStringEnum在JPA实体@Convert转换过程中，在向父类转换完后又需赋值时的报错异常
 *      
 *
 */

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.property.access.spi;

import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Locale;

import org.hibernate.PropertyAccessException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.property.access.internal.AbstractFieldSerialForm;

import com.peaksport.framework.base.venum.BaseIntEnum;
import com.peaksport.framework.base.venum.BaseStringEnum;

/**
 * Field-based implementation of Setter
 *
 * @author Steve Ebersole
 */
public class SetterFieldImpl implements Setter {
	private final Class containerClass;
	private final String propertyName;
	private final Field field;
	private final Method setterMethod;

	public SetterFieldImpl(Class containerClass, String propertyName, Field field) {
		this.containerClass = containerClass;
		this.propertyName = propertyName;
		this.field = field;
		this.setterMethod = ReflectHelper.setterMethodOrNull( containerClass, propertyName, field.getType() );
	}

	@Override
	public void set(Object target, Object value, SessionFactoryImplementor factory) {
		try {
			//modify by jinbin_guo 2019-06-05  为解决@Convert转换过程后,赋值时的报错异常
			// value被转换成基本的枚举类型BaseIntEnum,BaseStringEnum
			//这时需要将value的类型转换回来
			Class valueClass = value == null ? null : value.getClass();
			Class fieldClass = field.getType();
			if ((BaseIntEnum.class.isAssignableFrom(fieldClass) || BaseStringEnum.class.isAssignableFrom(fieldClass)) && valueClass.isAssignableFrom(fieldClass)) {
				if (value instanceof BaseIntEnum) {
					value = BaseIntEnum.getEnum(fieldClass, ((BaseIntEnum)value).getValue());
				} else if (value instanceof BaseStringEnum) {
					value = BaseStringEnum.getEnum(fieldClass, ((BaseStringEnum)value).getValue());
				}
			} 
			
			field.set( target, value );
		}
		catch (Exception e) {
			if (value == null && field.getType().isPrimitive()) {
				throw new PropertyAccessException(
						e,
						String.format(
								Locale.ROOT,
								"Null value was assigned to a property [%s.%s] of primitive type",
								containerClass,
								propertyName
						),
						true,
						containerClass,
						propertyName
				);
			}
			else {
				throw new PropertyAccessException(
						e,
						String.format(
								Locale.ROOT,
								"Could not set field value [%s] value by reflection : [%s.%s]",
								value,
								containerClass,
								propertyName
						),
						true,
						containerClass,
						propertyName
				);
			}
		}
	}

	@Override
	public String getMethodName() {
		return setterMethod != null ? setterMethod.getName() : null;
	}

	@Override
	public Method getMethod() {
		return setterMethod;
	}

	private Object writeReplace() throws ObjectStreamException {
		return new SerialForm( containerClass, propertyName, field );
	}

	private static class SerialForm extends AbstractFieldSerialForm implements Serializable {
		private final Class containerClass;
		private final String propertyName;


		private SerialForm(Class containerClass, String propertyName, Field field) {
			super( field );
			this.containerClass = containerClass;
			this.propertyName = propertyName;
		}

		private Object readResolve() {
			return new SetterFieldImpl( containerClass, propertyName, resolveField() );
		}

	}
}
